
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI聊天界面</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/5.1.1/marked.min.js"></script>
    <script src="static/scripts/jquery.min.js"></script>    
    <style>
        .message-box {
            max-width: 85%;
        }
        .user-message-box {
            background-color: #dbeafe;
            color: #1e3a8a;
            border-top-right-radius: 0;
        }
        .assistant-message-box {
            background-color: #dcfce7;
            color: #065f46;
            border-top-left-radius: 0;
        }
        .copy-button {
            opacity: 0;
            transition: opacity 0.2s;
        }
        .message-container:hover .copy-button {
            opacity: 1;
        }
        .hljs {
            padding: 1.25rem;
            border-radius: 0.375rem;
        }
        table {
            border-collapse: collapse;
            width: 100%;
        }
        td, th {
            border: 1px solid #ddd;
            padding: 8px;
        }
        @keyframes pulse {
            0%, 100% { opacity: 0.6; }
            50% { opacity: 1; }
        }
        .typing-indicator {
            animation: pulse 1.5s infinite;
        }
        .message-transition {
            transition: all 0.3s ease-out;
            opacity: 0;
            transform: translateY(10px);
        }
        .message-visible {
            opacity: 1;
            transform: translateY(0);
        }
        @keyframes cursorBlink {
            0%, 100% { opacity: 1; }
            50% { opacity: 0; }
        }
        .cursor {
            display: inline-block;
            width: 8px;
            height: 1.2em;
            background-color: currentColor;
            vertical-align: middle;
            margin-left: 2px;
            animation: cursorBlink 1s step-end infinite;
        }
        .message-content {
            overflow-wrap: break-word;
            white-space: pre-wrap;
        }
    </style>
    
    <!-- Marked配置 -->
    <script>
    // 配置Marked.js
    marked.setOptions({
        gfm: true,
        breaks: true,
        smartLists: true,
        smartypants: true,
        // 自定义渲染器以实现特定功能
        renderer: new marked.Renderer()
    });
    </script>
</head>
<body class="bg-gray-100 min-h-screen flex items-center justify-center p-4">
    <div class="flex flex-col h-full w-full max-w-3xl bg-white rounded-lg border shadow-sm" style="height: 90vh;">
        <!-- 标题栏 -->
        <div class="border-b p-4 flex items-center justify-between">
            <h1 class="text-xl font-bold flex items-center gap-2">
                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bot text-blue-500">
                    <path d="M12 8V4H8"/>
                    <rect width="16" height="12" x="4" y="8" rx="2"/>
                    <path d="M2 14h2"/>
                    <path d="M20 14h2"/>
                    <path d="M15 13v2"/>
                    <path d="M9 13v2"/>
                </svg>
                AI聊天助手
            </h1>
            <div class="flex items-center gap-3">
                <div class="w-3 h-3 rounded-full bg-green-500"></div>
                <span class="text-sm text-gray-500">已连接</span>
            </div>
        </div>
        
        <!-- 消息容器 -->
        <div id="messages-container" class="flex-1 overflow-y-auto p-4 space-y-6"></div>
                
        <!-- AI模型选择下拉框 -->
        <div class="border-t p-4 bg-gray-50" style="padding-bottom: 4px;">
            <select id="ai-model-select" class="border rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 text-gray-800 text-sm">
                <!-- 初始选项将被动态替换 -->
                <option value="" disabled selected>正在加载模型...</option>
            </select>
        </div>
                        
        <!-- 输入区域 -->
        <div class="p-4 bg-gray-50" style="padding-top: 0;">
            <form id="chat-form" class="flex gap-2 items-end">
                <textarea
                    id="message-input"
                    placeholder="输入消息..."
                    rows="1"
                    class="flex-1 border rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none text-base leading-6"
                    style="font-family:D-DINExp, DM Sans, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;"
                ></textarea>
                <button
                    type="submit"
                    class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-6 rounded-lg flex items-center gap-2 transition-colors"
                >
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-send">
                        <path d="m22 2-7 20-4-9-9-4Z"/>
                        <path d="M22 2 11 13"/>
                    </svg>
                    发送
                </button>
            </form>
            <p class="text-xs text-gray-500 mt-2">AI助手的回复不一定正确，请注意甄别</p>
        </div>
    </div>

    <script>
	    // 模型数据结构
	    var gAiModelList = [];
	    
	    const modelSelect = document.getElementById('ai-model-select');
	    
        // 模拟数据库
        const knowledgeBase = {
            "greeting": `您好！我是AI助手，随时为您提供帮助。您有任何问题都可以向我提问。`,
        };

        // 可能的响应数据
        const responses = {
            "greeting": knowledgeBase.greeting,
        };

        // 初始化Highlight.js
        hljs.highlightAll();

        // DOM元素引用
        const messagesContainer = document.getElementById('messages-container');
        const chatForm = document.getElementById('chat-form');
        const messageInput = document.getElementById('message-input');

        // 初始消息
        let messages = [
            { id: "1", content: responses.greeting, role: "assistant" },
        ];

        // 为代码块添加复制按钮的函数
        function addCopyButtonToCodeBlock(codeBlock) {
            if (codeBlock.parentElement.querySelector('.copy-button')) return;
            
            const preElement = codeBlock.closest('pre');
            if (!preElement) return;
            
            const container = document.createElement('div');
            container.className = 'relative';
            
            const button = document.createElement('button');
            button.className = 'copy-button absolute top-2 right-2 bg-white hover:bg-gray-100 rounded p-1 shadow';
            button.innerHTML = `
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy">
                    <rect width="14" height="14" x="8" y="8" rx="2" ry="2"/>
                    <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>
                </svg>
            `;
            
            // 复制功能
            button.addEventListener('click', () => {
                const code = codeBlock.innerText;
                navigator.clipboard.writeText(code);
                
                // 显示成功反馈
                button.innerHTML = `
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#10b981" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check">
                        <path d="M20 6 9 17l-5-5"/>
                    </svg>
                `;
                setTimeout(() => {
                    button.innerHTML = `
                        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy">
                            <rect width="14" height="14" x="8" y="8" rx="2" ry="2"/>
                            <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>
                        </svg>
                    `;
                }, 1500);
            });
            
            container.appendChild(preElement.cloneNode(true));
            container.appendChild(button);
            
            // 替换原始的pre元素
            preElement.replaceWith(container);
        }

        // 渲染消息
        function renderMessages() {
            messagesContainer.innerHTML = '';
            messages.forEach(message => {
                const messageElement = document.createElement('div');
                messageElement.id = `message-${message.id}`;
                
                messageElement.className = `flex flex-col group message-container ${message.role === 'user' ? 'items-end' : 'items-start'} message-transition`;
                
                const messageHeader = document.createElement('div');
                messageHeader.className = 'flex items-start gap-2 mb-1';
                
                // 图标
                const icon = document.createElement('div');
                icon.className = 'mt-1 flex-shrink-0';
                if (message.role === 'user') {
                    icon.innerHTML = `
                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-user text-blue-500">
                            <path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/>
                            <circle cx="12" cy="7" r="4"/>
                        </svg>
                    `;
                } else {
                    icon.innerHTML = `
                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bot text-green-500">
                            <path d="M12 8V4H8"/>
                            <rect width="16" height="12" x="4" y="8" rx="2"/>
                            <path d="M2 14h2"/>
                            <path d="M20 14h2"/>
                            <path d="M15 13v2"/>
                            <path d="M9 13v2"/>
                        </svg>
                    `;
                }
                
                const roleText = document.createElement('span');
                roleText.className = 'font-medium';
                roleText.textContent = message.role === 'user' ? '您' : 'AI助手';
                
                messageHeader.appendChild(icon);
                messageHeader.appendChild(roleText);
                
                // 消息内容 - 修复：添加 message-content 类名
                const contentContainer = document.createElement('div');
                contentContainer.className = `message-box p-4 rounded-lg prose message-content ${
                    message.role === 'user' 
                        ? 'user-message-box bg-blue-100 text-blue-900' 
                        : 'assistant-message-box bg-green-100 text-green-900'
                }`;
                
                // 解析Markdown内容
                const parsedContent = marked.parse(message.content);
                contentContainer.innerHTML = parsedContent;
                
                // 高亮代码块并添加复制按钮
                contentContainer.querySelectorAll('pre code').forEach(block => {
                    hljs.highlightElement(block);
                    addCopyButtonToCodeBlock(block);
                });
                
                // 组合元素
                messageElement.appendChild(messageHeader);
                messageElement.appendChild(contentContainer);
                messagesContainer.appendChild(messageElement);
                
                // 添加动画类触发过渡
                setTimeout(() => {
                    messageElement.classList.add('message-visible');
                }, 50);
            });
            
            // 滚动到底部
            messagesContainer.scrollTop = messagesContainer.scrollHeight;
        }

        // 流式接收消息的函数
        async function streamResponse(userInput, messageId, model) {
            try {
                const url = "/DocSystem/Repos/AIChat.do";
                const response = await fetch(url, {
                    method: 'POST',
                    headers: { 
                        'Content-Type': 'application/json',
                        'Accept': 'text/event-stream'
                    },
                    body: JSON.stringify({
                        query: userInput,
                        LLMIndex: model.LLMIndex,
                        LLMName: model.LLMName,
                        stream: true
                    })
                });
        
                if (!response.ok) {
                    throw new Error(`请求失败：${response.status}`);
                }
        
                const reader = response.body.getReader();
                const decoder = new TextDecoder("utf-8");
                let accumulatedContent = '';
                let buffer = '';
        
                // 设置超时
                const timeout = setTimeout(() => {
                    addErrorIndicator(messageId, "请求超时");
                    reader.cancel();
                }, 30000);
        
                // 查找目标消息
                const targetMessage = messages.find(msg => msg.id === messageId);
                if (!targetMessage) return;
                
                // 初始光标状态
                targetMessage.content = '<div class="cursor"></div>';
                renderMessages();
        
                while (true) {
                    const { value, done } = await reader.read();
                    if (done) break;
                    clearTimeout(timeout);
                    
                    var received_data = decoder.decode(value, { stream: true });
                    console.log("received_data:" + received_data);                    
                    buffer += received_data;
                    
                    // 处理缓冲区中的事件
                    while (buffer.includes('\n\n')) 
                    {
                        console.log("开始处理 buffer:" + buffer);
                        const eventEndIndex = buffer.indexOf('\n\n');
                        const eventChunk = buffer.substring(0, eventEndIndex);
                        console.log("eventChunk:" + eventChunk);
                        
                        buffer = buffer.substring(eventEndIndex + 2);
                        
                        if (!eventChunk.trim()) continue;
                        
                        // 提取数据字段
                        //const dataLines = eventChunk.split('\n')
                        //    .filter(line => line.startsWith('data:'))
                        //    .map(line => line.substring(5).trim());
                        //const eventData = dataLines.join('\n');
                        // 逐行处理引号问题
                        let eventData = '';
                        const lines = eventChunk.split('\n');
                        for (const line of lines) {
                            if (line.startsWith('data:')) {
                                let content = line.substring(5).trim();
                                
                                // 去掉可能的引号
                                if ((content.startsWith('"') && content.endsWith('"')) || 
                                    (content.startsWith("'") && content.endsWith("'"))) {
                                    content = content.substring(1, content.length - 1);
                                }
                                // 替换转义序列
								content = content.replace(/\\n/g, '\n')
								                 .replace(/\\t/g, '\t')
								                 .replace(/\\r/g, '\r')
								                 .replace(/\\b/g, '\b')
								                 .replace(/\\f/g, '\f')
								                 .replace(/\\\\/g, '\\');  // 两个反斜杠替换为一个
                                eventData += content;
                            }
                        }
                        
                        // 打印接收到的数据块 (调试用)
                        console.log('Received data chunk:', eventData);
                        
                        // 合并到消息内容
                        accumulatedContent += eventData;
                        
                        // 更新消息内容
                        targetMessage.content = accumulatedContent;
                        
                        // 渲染更新后的消息
                        renderMessageById(messageId);
                    }
                }
        
                // 处理剩余内容
                if (buffer) {
                    accumulatedContent += buffer;
                    targetMessage.content = accumulatedContent;
                    renderMessageById(messageId);
                }
                
                // 完成后移除光标
                targetMessage.content = accumulatedContent;
                renderMessages();
                
            } catch (error) {
                console.error('流式响应错误:', error);
                addErrorIndicator(messageId, error.message);
            }
        }
        
        // 渲染指定ID的消息（修复后的函数）
        function renderMessageById(messageId) {
            const message = messages.find(msg => msg.id === messageId);
            if (!message) return;
            
            const messageElement = document.getElementById(`message-${messageId}`);
            if (!messageElement) return;
            
            // 找到内容容器（使用正确的类名）
            const contentContainer = messageElement.querySelector('.message-content');
            if (!contentContainer) return;
            
            // 解析Markdown并设置内容
            const htmlContent = marked.parse(message.content) || '';
            contentContainer.innerHTML = htmlContent;
            
            // 高亮代码块并添加复制按钮
            contentContainer.querySelectorAll('pre code').forEach(block => {
                hljs.highlightElement(block);
                addCopyButtonToCodeBlock(block);
            });
            
            // 高亮其他可能的内容
            hljs.highlightAll();
            
            // 滚动到最新消息
            messageElement.scrollIntoView({ behavior: 'smooth', block: 'end' });
        }
        
        // 添加错误指示器
        function addErrorIndicator(messageId, errorMsg = "请求出错") {
            messages = messages.map(msg => 
                msg.id === messageId 
                    ? {...msg, content: `⚠️ <strong>${errorMsg}</strong> 请重试`} 
                    : msg
            );
            renderMessages();
        }
        
        // 表单提交处理
        chatForm.addEventListener('submit', async (e) => {
            e.preventDefault();
            const input = messageInput.value.trim();
            if (!input) return;
            
            // 检查模型是否已加载
            if (!gAiModelList.length || modelSelect.value === "") {
                messages.push({
                    id: Date.now().toString(),
                    content: "⚠️ 模型列表尚未加载完成，请稍后再试",
                    role: "assistant"
                });
                renderMessages();
                return;
            }
            
            // 获取选中的模型
            const selectedModel = gAiModelList.find(m => m.LLMIndex == modelSelect.value);
            
            // 添加用户消息
            const newUserMessage = {
                id: Date.now().toString(),
                content: input,
                role: "user"
            };
            messages = [...messages, newUserMessage];
            messageInput.value = '';
            renderMessages();
            
            // 添加AI消息（初始状态带光标）
            const aiMessageId = (Date.now() + 1).toString();
            const newAssistantMessage = {
                id: aiMessageId,
                content: '<div class="cursor"></div>',
                role: "assistant"
            };
            messages = [...messages, newAssistantMessage];
            renderMessages();
            
            // 发起流式请求
            await streamResponse(input, aiMessageId, selectedModel);
        });


        
        // 动态填充模型选择下拉框
        function populateModelSelect(models) {
            modelSelect.innerHTML = ''; // 清空加载中的消息
            
            models.forEach(model => {
                const option = document.createElement('option');
                option.value = model.LLMIndex;
                option.textContent = model.LLMName;
                modelSelect.appendChild(option);
            });
        }
        
        function buildAiModelList(modelList)
        {
        	var models = [];
        	for(var i=0; i<modelList.length; i++)
        	{
        		var model = {};
        		model.LLMIndex = i;
        		model.LLMName = modelList[i];
        		models.push(model);
        	}
        	return models;
        }
        
        //获取Ai模型列表
        function getAiModelList()
        {
        	console.log("getAiModelList");
            $.ajax({
                url : "/DocSystem/Repos/getAiModelList.do",
                type :"post",
                dataType :"json",
                data : null,
                success : function (ret) {
                    if(ret.status == "ok")
                    {
                    	gAiModelList = buildAiModelList(ret.data);
                    	console.log("getAiModelList aiModelList:", gAiModelList);
                    	populateModelSelect(gAiModelList); //更新模型选择列表
                    }
                    else
                    {
                    	console.log(ret.msgInfo);
                    }
                },
                error : function () {
                	console.log('服务器异常:获取Ai模型列表失败');
                }
            });
        }
        
        // 初始化页面
        document.addEventListener('DOMContentLoaded', () => {
            
        	// 加载模型列表
            getAiModelList();
        	
            // textarea 自适应高度 + Enter快捷键发送
            const textarea = document.getElementById('message-input');
            textarea.addEventListener('input', () => {
                textarea.style.height = 'auto'; // 先重置再设
                textarea.style.height = (textarea.scrollHeight) + 'px';
            });
            
            renderMessages();           
        });
    </script>
</body>
</html>
